// (c) Ivan Gagis 2008
// e-mail: igagis@gmail.com

// Description:
//          cross platfrom C++ Sockets wrapper
//
//      Some code was taken from SDL_net library (http://libsdl.org),
//      so, this wrapper inherits the LGPL license from SDL_net.
//

/**
@file sckt.hpp
@brief Main header file of the library.
This is the main header file of sckt. You need to include it to use sckt library.
*/

#ifndef M_SCKT_HPP
#define M_SCKT_HPP

#if defined(M_BUILD_DLL) & defined(__WIN32__)
#define M_DECLSPEC __declspec(dllexport)
#else
#define M_DECLSPEC
#endif

#include <exception>
#include <new>

/**
@brief the main namespace of sckt library.
All the declarations of sckt library are made inside this namespace.
*/
namespace sckt{

//some typedefs, these are system specific, so maybe there will appear some #ifdef s in the future, for different systems.
typedef unsigned int uint;///< platform native unsigned integer
typedef unsigned int u32;///< 32 bit unsigned integer
typedef unsigned short u16;///< 16 bit unsigned integer
typedef unsigned char byte;///< 8bit unsigned integer

//forward declarations
class C_SocketSet;
class C_IPAddress;

/**
@brief Basic exception class.
This is a basic exception class of the library. All other exception classes are derived from it.
*/
class M_DECLSPEC C_Exc : public std::exception{
    char *msg;
public:
    /**
    @brief Exception constructor.
    @param message Pointer to the exception message null-terminated string. Constructor will copy the string into objects internal memory buffer.
    */
    C_Exc(const char* message = 0) throw(std::bad_alloc);
    virtual ~C_Exc()throw();
    
    /**
    @brief Returns a pointer to exception message.
    @return a pointer to objects internal memory buffer holding the exception message null-terminated string.
            Note, that after the exception object is destroyed the pointer returned by this method become invalid.
    */
    const char *What()const throw(){
        return this->msg?this->msg:("sckt::C_Exc(): Unknown exception");
    };
    
private:
    //override from std::exception
    const char *what()const throw(){
        return this->What();
    };
};

/**
@brief a structure which holds IP address
*/
class M_DECLSPEC C_IPAddress{
public:
    u32 host;///< IP address
    u16 port;///< IP port number
    
    inline C_IPAddress()throw();
    
    /**
    @brief Create IP address specifying exact ip address and port number.
    @param h - IP address. For example, 0x100007f represents "127.0.0.1" IP address value.
    @param p - IP port number.
    */
    inline C_IPAddress(u32 h, u16 p) throw() :
            host(h),
            port(p)
    {};
    
    /**
    @brief Create IP address specifying ip address as string and port number.
    @param ip - IP address null-terminated string. Example: "127.0.0.1".
    @param p - IP port number.
    */
    inline C_IPAddress(const char* ip, u16 p) throw(sckt::C_Exc) :
            host(C_IPAddress::ParseString(ip)),
            port(p)
    {};
    
    /**
    @brief compares two IP addresses for equality.
    @param ip - IP address to compare with.
    @return true if hosts and ports of the two IP addresses are equal accordingly.
    @return false otherwise.
    */
    inline bool operator==(const C_IPAddress& ip) throw(){
        return (this->host == ip.host) && (this->port == ip.port);
    }
private:
    //parse IP address from string
    static u32 ParseString(const char* ip) throw(sckt::C_Exc);
};

/**
@brief Library singletone class.
This is a library singletone class. Creating an object of this class initializes the library
while destroying this object deinitializes it. So, the convenient way of initializing the library
is to create an object of this class on the stack. Thus, when the object goes out of scope its
destructor will be called and the library will be deinitialized automatically.
This is what C++ RAII is all about ;-).
*/
//singletone
class M_DECLSPEC C_Sockets{
    static C_Sockets *instance;
public:
    C_Sockets()throw(sckt::C_Exc);
    ~C_Sockets()throw();
    
    /**
    @brief Get reference to singletone object.
    This static method returns a reference to C_Sockets singletone object.
    If the object was not created before then this function will throw sckt::C_Exc.
    @return reference to C_Sockets singletone object.
    */
    static C_Sockets& Inst()throw(sckt::C_Exc){
        if(!C_Sockets::instance)
            throw sckt::C_Exc("C_Sockets::Inst(): singletone C_Sockets object is not created");
        return *C_Sockets::instance;
    };
    
    /**
    @brief Resolve host IP by its name.
    This function resolves host IP address by its name. If it fails resolving the IP address it will throw sckt::C_Exc.
    @param hostName - null-terminated string representing host name. Example: "www.somedomain.com".
    @param port - IP port number which will be placed in the resulting C_IPAddress structure.
    @return filled C_IPAddress structure.
    */
    C_IPAddress GetHostByName(const char *hostName, u16 port)throw(sckt::C_Exc);
private:
    static void InitSockets()throw(sckt::C_Exc);
    static void DeinitSockets()throw();
};

/**
@brief Basic socket class.
This is a base class for all socket types such as TCP sockets or UDP sockets.
*/
class M_DECLSPEC C_Socket{
    friend class C_SocketSet;
    
public:
    //this type will hold system specific socket handle.
    //this buffer should be large enough to hold socket handle in different systems.
    //sizeof(int) looks enough so far.
#ifndef DOC_DONT_EXTRACT //direction to doxygen not to extract this class
    struct C_SystemIndependentSocketHandle{
        sckt::byte buffer[sizeof(int)];
    };
#endif//~DOC_DONT_EXTRACT

protected:
    bool isReady;
    
    C_SystemIndependentSocketHandle socket;
    
    C_Socket() throw();
    
    C_Socket& operator=(const C_Socket& s) throw();
    
public:
    virtual ~C_Socket(){
        this->Close();
    };
    
    /**
    @brief Tells whether the socket is opened or not.
    TODO: write some detailed description.
    @return Returns true if the socket is opened or false otherwise.
    */
    bool IsValid()const throw();
    
    /**
    @brief Tells whether there is some activity on the socket or not.
    See sckt::C_SocketSet class description for more info on how to use this method properly.
    @return
        - true if there is some data to read on the socket or if the remote socket has disconnected.
            The latter case can be detected by checking if subsequent Recv() method returns 0.
        - false if there is no any activity on the socket.
    */
    inline bool IsReady()const throw(){
        return this->isReady;
    };
    
    /**
    @brief Closes the socket disconnecting it if necessary.
    */
    void Close() throw();
};

/**
@brief a class which represents a TCP socket.
*/
class M_DECLSPEC C_TCPSocket : public C_Socket{
    friend class C_TCPServerSocket;
public:
    /**
    @brief Constructs an invalid TCP socket object.
    */
    C_TCPSocket() throw(){};
    
    /**
    @brief A copy constructor.
    Copy constructor creates a new socket object which refers to the same socket as s.
    After constructor completes the s becomes invalid.
    In other words, the behavior of copy constructor is similar to one of std::auto_ptr class from standard C++ library.
    @param s - other TCP socket to make a copy from.
    */
    //copy constructor
    C_TCPSocket(const C_TCPSocket& s) throw(){
        //NOTE: that operator= calls destructor, so this->socket should be invalid, base class constructor takes care about it.
        this->operator=(s);//same as auto_ptr
    };
    
    /**
    @brief A constructor which automatically calls sckt::C_TCPSocket::Open() method.
    This constructor creates a socket and calls its sckt::C_TCPSocket::Open() method.
    So, it creates an already opened socket.
    @param ip - IP address to 'connect to/listen on'.
    @param disableNaggle - enable/disable Naggle algorithm.
    */
    C_TCPSocket(const C_IPAddress& ip, bool disableNaggle = false) throw(sckt::C_Exc){
        this->Open(ip, disableNaggle);
    };
    
    /**
    @brief Assignment operator, works similar to std::auto_ptr::operator=().
    After this assignment operator completes this socket object refers to the socket the s objejct referred, s become invalid.
    It works similar to std::auto_ptr::operator=() from standard C++ library.
    @param s - socket to assign from.
    */
    C_TCPSocket& operator=(const C_TCPSocket& s) throw(){
        this->C_Socket::operator=(s);
        return *this;
    };
    
    /**
    @brief Connects the socket.
    This method connects the socket to remote TCP server socket.
    @param ip - IP address.
    @param disableNaggle - enable/disable Naggle algorithm.
    */
    void Open(const C_IPAddress& ip, bool disableNaggle = false) throw(sckt::C_Exc);
    
    /**
    @brief Send data to connected socket.
    Sends data on connected socket. This method blocks until all data is completely sent.
    @param data - pointer to the buffer with data to send.
    @param size - number of bytes to send.
    @return the number of bytes sent. Note that this value should normally be equal to the size argument value.
    */
    uint Send(const byte* data, uint size) throw(sckt::C_Exc);
    
    /**
    @brief Receive data from connected socket.
    Receives data available on the socket.
    If there is no data available this function blocks until some data arrives.
    @param buf - pointer to the buffer where to put received data.
    @param maxSize - maximal number of bytes which can be put to the buffer.
    @return if returned value is not 0 then it represents the number of bytes written to the buffer.
    @return 0 returned value indicates disconnection of remote socket.
    */
    //returns 0 if connection was closed by peer
    uint Recv(byte* buf, uint maxSize) throw(sckt::C_Exc);
    
private:
    void DisableNaggle() throw(sckt::C_Exc);
};

/**
@brief a class which represents a TCP server socket.
TCP server socket is the socket which can listen for new connections
and accept them creating an ordinary TCP socket for it.
*/
class M_DECLSPEC C_TCPServerSocket : public C_Socket{
    bool disableNaggle;//this flag indicates if accepted sockets should be created with disabled Naggle
public:
    /**
    @brief Creates an invalid (unopened) TCP server socket.
    */
    C_TCPServerSocket() throw() :
            disableNaggle(false)
    {};
    
    /**
    @brief A copy constructor.
    Copy constructor creates a new socket object which refers to the same socket as s.
    After constructor completes the s becomes invalid.
    In other words, the behavior of copy constructor is similar to one of std::auto_ptr class from standard C++ library.
    @param s - other TCP socket to make a copy from.
    */
    //copy constructor
    C_TCPServerSocket(const C_TCPServerSocket& s) throw() :
            disableNaggle(s.disableNaggle)
    {
        //NOTE: that operator= calls destructor, so this->socket should be invalid, base class constructor takes care about it.
        this->operator=(s);//same as auto_ptr
    };
    
    /**
    @brief Assignment operator, works similar to std::auto_ptr::operator=().
    After this assignment operator completes this socket object refers to the socket the s objejct referred, s become invalid.
    It works similar to std::auto_ptr::operator=() from standard C++ library.
    @param s - socket to assign from.
    */
    C_TCPServerSocket& operator=(const C_TCPServerSocket& s) throw(){
        this->C_Socket::operator=(s);
        return *this;
    };
    
    /**
    @brief A constructor which automatically calls sckt::C_TCPServerSocket::Open() method.
    This constructor creates a socket and calls its sckt::C_TCPServerSocket::Open() method.
    So, it creates an already opened socket listening on the specified port.
    @param port - IP port number to listen on.
    @param disableNaggle - enable/disable Naggle algorithm for all accepted connections.
    */
    C_TCPServerSocket(u16 port, bool disableNaggle = false) throw(sckt::C_Exc){
        this->Open(port, disableNaggle);
    };
    
    /**
    @brief Connects the socket or starts listening on it.
    This method starts listening on the socket for incoming connections.
    @param port - IP port number to listen on.
    @param disableNaggle - enable/disable Naggle algorithm for all accepted connections.
    */
    void Open(u16 port, bool disableNaggle = false) throw(sckt::C_Exc);
    
    /**
    @brief Accepts one of the pending connections, non-blocking.
    Accepts one of the pending connections and returns a TCP socket object which represents
    either a valid connected socket or an invalid socket object.
    This function does not block if there is no any pending connections, it just returns invalid
    socket object in this case. One can periodically check for incoming connections by calling this method.
    @return sckt::C_TCPSocket object. One can later check if the returned socket object
        is valid or not by calling sckt::C_Socket::IsValid() method on that object.
        - if the socket is valid then it is a newly connected socket, further it can be used to send or receive data.
        - if the socket is invalid then there was no any connections pending, so no connection was accepted.
    */
    C_TCPSocket Accept() throw(sckt::C_Exc);
};

class M_DECLSPEC C_UDPSocket : public C_Socket{
public:
    C_UDPSocket()throw(){};
    
    ~C_UDPSocket()throw(){
        this->Close();
    };
    
    /**
    @brief Open the socket.
    This mthod opens the socket, this socket can further be used to send or receive data.
    After the socket is opened it becomes a valid socket and C_Socket::IsValid() will return true for such socket.
    After the socket is closed it becomes invalid.
    In other words, a valid socket is an opened socket.
    In case of errors this method throws sckt::C_Exc.
    @param port - IP port number on which the socket will listen for incoming datagrams.
        This is useful for server-side sockets, for client-side sockets use C_UDPSocket::Open().
    */
    void Open(u16 port) throw(sckt::C_Exc);
    
    
    inline void Open() throw(sckt::C_Exc){
        this->Open(0);
    };
    
    //returns number of bytes sent, should be less or equal to size.
    uint Send(const byte* buf, u16 size, C_IPAddress destinationIP) throw(sckt::C_Exc);
    
    //returns number of bytes received, 0 if connection was gracefully closed (???).
    uint Recv(byte* buf, u16 maxSize, C_IPAddress &out_SenderIP) throw(sckt::C_Exc);
};


/**
@brief Socket set class for checking multiple sockets for activity.
This class represents a set of sockets which can be checked for any activity
such as incoming data received or remote socket has disconnected.
Note, that the socket set holds only references to socket objects, so it is up to you
to make sure that all the socket objects you add to a particular socket set will not be
destroyed without prior removing them from socket set.
*/
class M_DECLSPEC C_SocketSet{
    C_Socket** set;
    uint maxSockets;
    uint numSockets;
  public:
    
    /**
    @brief Creates a socket set of the specified size.
    Creates a socket set which can hold the specified number of sockets at maximum.
    @param maxNumSocks - maximum number of sockets this socket set can hold.
    */
    C_SocketSet(uint maxNumSocks) throw(sckt::C_Exc, std::bad_alloc);
    
    /**
    @brief Destroys the socket set.
    Note, that it does not destroy the sockets this set holds references to.
    */
    ~C_SocketSet() throw(){
        delete[] set;
    };
    
    /**
    @brief Returns number of sockets the socket set currently holds.
    @return number of sockets the socket set currently holds.
    */
    inline uint NumSockets()const throw(){return this->numSockets;};
    
    /**
    @brief Returns maximal number of sockets this set can hold.
    @return maximal number of sockets this set can hold.
    */
    inline uint MaxSockets()const throw(){return this->maxSockets;};
    
    /**
    @brief Add a socket to socket set.
    @param sock - pointer to the socket object to add.
    */
    void AddSocket(C_Socket *sock) throw(sckt::C_Exc);
    
    /**
    @brief Remove socket from socket set.
    @param sock - pointer to socket object which we want to remove from the set.
    */
    void RemoveSocket(C_Socket *sock) throw(sckt::C_Exc);
    
    /**
    @brief Check sokets from socket set for activity.
    This method checks sockets for activities of incoming data ready or remote socket has disconnected.
    This method sets ready flag for all sockets with activity which can later be checked by
    sckt::C_Socket::IsReady() method. The ready flag will be cleared by subsequent sckt::C_TCPSocket::Recv() function call.
    @param timeoutMillis - maximum number of milliseconds to wait for socket activity to appear.
        if 0 is specified the function will not wait and will return immediately.
    @return true if there is at least one socket with activity.
    @return false if there are no any sockets with activities.
    */
    //This function checks to see if data is available for reading on the
    //given set of sockets.  If 'timeout' is 0, it performs a quick poll,
    //otherwise the function returns when either data is available for
    //reading, or the timeout in milliseconds has elapsed, which ever occurs
    //first.  This function returns true if there are any sockets ready for reading,
    //or false if there was an error with the select() system call.
    bool CheckSockets(uint timeoutMillis) throw();
};

};//~namespace sckt
#endif//~once


/**

@mainpage sckt library

@section sec_about About
<b>sckt</b> is a simple cross platfrom C++ wrapper above sockets networking API designed for games.

@section sec_getting_started Getting started
@ref page_usage_tutorial "library usage tutorial" - quickstart tutorial
*/

/**
@page page_usage_tutorial sckt usage tutorial

TODO: write usage tutorial

*/
